package com.karrim.server.util.array;

import com.google.inject.Inject;
import com.karrim.server.annotation.Annotation.BufferCapacity;
import com.karrim.server.util.array.abstr.AbstractArray;
import com.karrim.server.util.array.def.Array;
import java.util.Iterator;

/**
 *
 * @author andrea.cantafio
 */
public class Buffer<T> extends AbstractArray<T> {

    @Inject
    public Buffer(@BufferCapacity int capacity) {
        ts = (T[]) new Object[capacity];
    }

    public Buffer(Array<T> array) {
        super(array);
    }

    @Override
    synchronized public Buffer add(T t) {
        while (size >= ts.length) {
            try {
                wait();
            } catch (InterruptedException ie) {
                break;
            }
        }
        ts[in++] = t;
        in %= ts.length;
        size++;
        notifyAll();
        return this;
    }

    synchronized public T get() {
        while (size <= 0) {
            try {
                wait();
            } catch (InterruptedException ie) {
                break;
            }
        }
        T t = ts[out++];
        out %= ts.length;
        size--;
        return t;
    }

    @Override
    public Integer getCapacity() {
        return ts.length;
    }

    @Override
    synchronized public Integer getSize() {
        return super.getSize();
    }

    @Override
    public Iterator<T> iterator() {
        /**
         * No need for synchronization: this instance of iterator calls
         * synchronized methods on parent class.
         */
        return new Iterator<T>() {
            @Override
            public boolean hasNext() {
                return getSize() > 0;
            }

            /**
             * Equivalent of parent class'
             * {@link com.karrim.server.util.array.Buffer} get() method
             */
            @Override
            public T next() {
                return get();
            }

            /**
             * Empty implementation. It doesn't make sense for this Buffer.
             */
            @Override
            public void remove() {
            }
        };
    }
    private int in = 0, out = 0;
    private T[] ts = (T[]) new Object[]{};
}
